@void:
-
-<!-- ##### FUNCTION gdk_error_trap_push ##### -->
-<para>
-This function allows X errors to be trapped instead of the normal behavior
-of exiting the application. It should only be used if it is not possible to
-avoid the X error in any other way.
-</para>
-<example>
-<title>Trapping an X error</title>
-<programlisting>
- gdk_error_trap_push (<!-- -->);
-
- /* ... Call the X function which may cause an error here ... */
-
- /* Flush the X queue to catch errors now. */
- gdk_flush (<!-- -->);
-
- if (gdk_error_trap_pop (<!-- -->))
- {
- /* ... Handle the error here ... */
- }
-</programlisting>
-</example>
-
-@void:
-
-
-<!-- ##### FUNCTION gdk_error_trap_pop ##### -->
-<para>
-Removes the X error trap installed with gdk_error_trap_push().
-</para>
-
-@void:
-@Returns: the X error code, or 0 if no error occurred.
-
-
<!-- ##### MACRO GDK_WINDOWING_X11 ##### -->
<para>
This macro is defined if GDK is configured to use the X11 backend.
/* Push and pop error handlers for X errors
*/
-void gdk_error_trap_push (void);
-gint gdk_error_trap_pop (void);
+void gdk_error_trap_push (void);
+/* warn unused because you could use pop_ignored otherwise */
+G_GNUC_WARN_UNUSED_RESULT gint gdk_error_trap_pop (void);
+void gdk_error_trap_pop_ignored (void);
+
gchar* gdk_get_display (void);
G_CONST_RETURN gchar* gdk_get_display_arg_name (void);
guint _gdk_debug_flags = 0;
-gint _gdk_error_code = 0;
-gint _gdk_error_warnings = TRUE;
GList *_gdk_default_filters = NULL;
gchar *_gdk_display_name = NULL;
gint _gdk_screen_number = -1;
extern GList *_gdk_default_filters;
extern GdkWindow *_gdk_parent_root;
-extern gint _gdk_error_code;
-extern gint _gdk_error_warnings;
extern guint _gdk_debug_flags;
extern gboolean _gdk_native_windows;
return 0;
}
+void
+gdk_error_trap_pop_ignored (void)
+{
+}
+
gchar *
gdk_get_display (void)
{
return 0;
}
+void
+gdk_error_trap_pop_ignored (void)
+{
+}
+
void
gdk_notify_startup_complete (void)
{
#include "config.h"
+#include <glib/gprintf.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <X11/extensions/Xrandr.h>
#endif
+typedef struct _GdkErrorTrap GdkErrorTrap;
+
+struct _GdkErrorTrap
+{
+ /* Next sequence when trap was pushed, i.e. first sequence to
+ * ignore
+ */
+ gulong start_sequence;
+
+ /* Next sequence when trap was popped, i.e. first sequence
+ * to not ignore. 0 if trap is still active.
+ */
+ gulong end_sequence;
+
+ /* Most recent error code within the sequence */
+ int error_code;
+};
static void gdk_display_x11_dispose (GObject *object);
static void gdk_display_x11_finalize (GObject *object);
display_x11->event_types = g_slist_prepend (display_x11->event_types, event_type);
}
+
+/* compare X sequence numbers handling wraparound */
+#define SEQUENCE_COMPARE(a,op,b) (((long) (a) - (long) (b)) op 0)
+
+/* delivers an error event from the error handler in gdkmain-x11.c */
+void
+_gdk_x11_display_error_event (GdkDisplay *display,
+ XErrorEvent *error)
+{
+ GdkDisplayX11 *display_x11;
+ GSList *tmp_list;
+ gboolean ignore;
+
+ display_x11 = GDK_DISPLAY_X11 (display);
+
+ ignore = FALSE;
+ for (tmp_list = display_x11->error_traps;
+ tmp_list != NULL;
+ tmp_list = tmp_list->next)
+ {
+ GdkErrorTrap *trap;
+
+ trap = tmp_list->data;
+
+ if (SEQUENCE_COMPARE (trap->start_sequence, <=, error->serial) &&
+ (trap->end_sequence == 0 ||
+ SEQUENCE_COMPARE (trap->end_sequence, >, error->serial)))
+ {
+ ignore = TRUE;
+ trap->error_code = error->error_code;
+ }
+ }
+
+ if (!ignore)
+ {
+ gchar buf[64];
+ gchar *msg;
+
+ XGetErrorText (display_x11->xdisplay, error->error_code, buf, 63);
+
+ msg =
+ g_strdup_printf ("The program '%s' received an X Window System error.\n"
+ "This probably reflects a bug in the program.\n"
+ "The error was '%s'.\n"
+ " (Details: serial %ld error_code %d request_code %d minor_code %d)\n"
+ " (Note to programmers: normally, X errors are reported asynchronously;\n"
+ " that is, you will receive the error a while after causing it.\n"
+ " To debug your program, run it with the --sync command line\n"
+ " option to change this behavior. You can then get a meaningful\n"
+ " backtrace from your debugger if you break on the gdk_x_error() function.)",
+ g_get_prgname (),
+ buf,
+ error->serial,
+ error->error_code,
+ error->request_code,
+ error->minor_code);
+
+#ifdef G_ENABLE_DEBUG
+ g_error ("%s", msg);
+#else /* !G_ENABLE_DEBUG */
+ g_fprintf (stderr, "%s\n", msg);
+
+ exit (1);
+#endif /* G_ENABLE_DEBUG */
+ }
+}
+
+static void
+delete_outdated_error_traps (GdkDisplayX11 *display_x11)
+{
+ GSList *tmp_list;
+ gulong processed_sequence;
+
+ processed_sequence = XLastKnownRequestProcessed (display_x11->xdisplay);
+
+ tmp_list = display_x11->error_traps;
+ while (tmp_list != NULL)
+ {
+ GdkErrorTrap *trap = tmp_list->data;
+
+ if (trap->end_sequence != 0 &&
+ SEQUENCE_COMPARE (trap->end_sequence, <, processed_sequence))
+ {
+ GSList *free_me = tmp_list;
+
+ tmp_list = tmp_list->next;
+ display_x11->error_traps =
+ g_slist_delete_link (display_x11->error_traps, free_me);
+ g_slice_free (GdkErrorTrap, trap);
+ }
+ else
+ {
+ tmp_list = tmp_list->next;
+ }
+ }
+}
+
+/**
+ * gdk_x11_display_error_trap_push:
+ *
+ * Begins a range of X requests for which X error events will be
+ * ignored. Unignored errors (when no trap is pushed) will abort the
+ * application.
+ *
+ * See also gdk_error_trap_push() to push a trap on all displays.
+ *
+ * Since: 3.0
+ */
+void
+gdk_x11_display_error_trap_push (GdkDisplay *display)
+{
+ GdkDisplayX11 *display_x11;
+ GdkErrorTrap *trap;
+
+ display_x11 = GDK_DISPLAY_X11 (display);
+
+ delete_outdated_error_traps (display_x11);
+
+ /* set up the Xlib callback to tell us about errors */
+ _gdk_x11_error_handler_push ();
+
+ trap = g_slice_new0 (GdkErrorTrap);
+
+ trap->start_sequence = XNextRequest (display_x11->xdisplay);
+ trap->error_code = Success;
+
+ display_x11->error_traps =
+ g_slist_prepend (display_x11->error_traps, trap);
+}
+
+static gint
+gdk_x11_display_error_trap_pop_internal (GdkDisplay *display,
+ gboolean need_code)
+{
+ GdkDisplayX11 *display_x11;
+ GdkErrorTrap *trap;
+ GSList *tmp_list;
+ int result;
+
+ display_x11 = GDK_DISPLAY_X11 (display);
+
+ g_return_val_if_fail (display_x11->error_traps != NULL, Success);
+
+ /* Find the first trap that hasn't been popped already */
+ trap = NULL; /* quiet gcc */
+ for (tmp_list = display_x11->error_traps;
+ tmp_list != NULL;
+ tmp_list = tmp_list->next)
+ {
+ trap = tmp_list->data;
+
+ if (trap->end_sequence == 0)
+ break;
+ }
+
+ g_return_val_if_fail (trap != NULL, Success);
+ g_assert (trap->end_sequence == 0);
+
+ /* May need to sync to fill in trap->error_code if we care about
+ * getting an error code.
+ */
+ if (need_code)
+ {
+ gulong processed_sequence;
+ gulong next_sequence;
+
+ next_sequence = XNextRequest (display_x11->xdisplay);
+ processed_sequence = XLastKnownRequestProcessed (display_x11->xdisplay);
+
+ /* If our last request was already processed, there is no point
+ * in syncing. i.e. if last request was a round trip (or even if
+ * we got an event with the serial of a non-round-trip)
+ */
+ if ((next_sequence - 1) != processed_sequence)
+ {
+ XSync (display_x11->xdisplay, False);
+ }
+
+ result = trap->error_code;
+ }
+ else
+ {
+ result = Success;
+ }
+
+ /* record end of trap, giving us a range of
+ * error sequences we'll ignore.
+ */
+ trap->end_sequence = XNextRequest (display_x11->xdisplay);
+
+ /* remove the Xlib callback */
+ _gdk_x11_error_handler_pop ();
+
+ /* we may already be outdated */
+ delete_outdated_error_traps (display_x11);
+
+ return result;
+}
+
+/**
+ * gdk_x11_display_error_trap_pop:
+ * @display: the display
+ *
+ * Pops the error trap pushed by gdk_x11_display_error_trap_push().
+ * Will XSync() if necessary and will always block until
+ * the error is known to have occurred or not occurred,
+ * so the error code can be returned.
+ *
+ * If you don't need to use the return value,
+ * gdk_x11_display_error_trap_pop_ignored() would be more efficient.
+ *
+ * See gdk_error_trap_pop() for the all-displays-at-once
+ * equivalent.
+ *
+ * Since: 3.0
+ *
+ * Return value: X error code or 0 on success
+ */
+gint
+gdk_x11_display_error_trap_pop (GdkDisplay *display)
+{
+ g_return_val_if_fail (GDK_IS_DISPLAY_X11 (display), Success);
+
+ return gdk_x11_display_error_trap_pop_internal (display, TRUE);
+}
+
+/**
+ * gdk_x11_display_error_trap_pop_ignored:
+ * @display: the display
+ *
+ * Pops the error trap pushed by gdk_x11_display_error_trap_push().
+ * Does not block to see if an error occurred; merely records the
+ * range of requests to ignore errors for, and ignores those errors
+ * if they arrive asynchronously.
+ *
+ * See gdk_error_trap_pop_ignored() for the all-displays-at-once
+ * equivalent.
+ *
+ * Since: 3.0
+ */
+void
+gdk_x11_display_error_trap_pop_ignored (GdkDisplay *display)
+{
+ g_return_if_fail (GDK_IS_DISPLAY_X11 (display));
+
+ gdk_x11_display_error_trap_pop_internal (display, FALSE);
+}
/* The offscreen window that has the pointer in it (if any) */
GdkWindow *active_offscreen_window;
+
+ GSList *error_traps;
};
struct _GdkDisplayX11Class
};
GType _gdk_display_x11_get_type (void);
-GdkScreen *_gdk_x11_display_screen_for_xrootwin (GdkDisplay *display,
- Window xrootwin);
+GdkScreen *_gdk_x11_display_screen_for_xrootwin (GdkDisplay *display,
+ Window xrootwin);
+void _gdk_x11_display_error_event (GdkDisplay *display,
+ XErrorEvent *error);
G_END_DECLS
#include <gdk/gdkdeviceprivate.h>
-typedef struct _GdkPredicate GdkPredicate;
-typedef struct _GdkErrorTrap GdkErrorTrap;
+typedef struct _GdkPredicate GdkPredicate;
+typedef struct _GdkGlobalErrorTrap GdkGlobalErrorTrap;
struct _GdkPredicate
{
gpointer data;
};
-struct _GdkErrorTrap
+/* non-GDK previous error handler */
+static int (*_gdk_old_error_handler) (Display *, XErrorEvent *);
+/* number of times we've pushed the GDK error handler */
+static int _gdk_error_handler_push_count = 0;
+
+struct _GdkGlobalErrorTrap
{
- int (*old_handler) (Display *, XErrorEvent *);
- gint error_warnings;
- gint error_code;
+ GSList *displays;
};
/*
}
}
-/*
- *--------------------------------------------------------------
- * gdk_x_error
- *
- * The X error handling routine.
- *
- * Arguments:
- * "display" is the X display the error originated from.
- * "error" is the XErrorEvent that we are handling.
- *
- * Results:
- * Either we were expecting some sort of error to occur,
- * in which case we set the "_gdk_error_code" flag, or this
- * error was unexpected, in which case we will print an
- * error message and exit. (Since trying to continue will
- * most likely simply lead to more errors).
- *
- * Side effects:
- *
- *--------------------------------------------------------------
- */
-
-static int
-gdk_x_error (Display *display,
- XErrorEvent *error)
-{
- if (error->error_code)
- {
- if (_gdk_error_warnings)
- {
- gchar buf[64];
- gchar *msg;
-
- XGetErrorText (display, error->error_code, buf, 63);
-
- msg =
- g_strdup_printf ("The program '%s' received an X Window System error.\n"
- "This probably reflects a bug in the program.\n"
- "The error was '%s'.\n"
- " (Details: serial %ld error_code %d request_code %d minor_code %d)\n"
- " (Note to programmers: normally, X errors are reported asynchronously;\n"
- " that is, you will receive the error a while after causing it.\n"
- " To debug your program, run it with the --sync command line\n"
- " option to change this behavior. You can then get a meaningful\n"
- " backtrace from your debugger if you break on the gdk_x_error() function.)",
- g_get_prgname (),
- buf,
- error->serial,
- error->error_code,
- error->request_code,
- error->minor_code);
-
-#ifdef G_ENABLE_DEBUG
- g_error ("%s", msg);
-#else /* !G_ENABLE_DEBUG */
- g_fprintf (stderr, "%s\n", msg);
-
- exit (1);
-#endif /* G_ENABLE_DEBUG */
- }
- _gdk_error_code = error->error_code;
- }
-
- return 0;
-}
-
/*
*--------------------------------------------------------------
* gdk_x_io_error
exit(1);
}
+/* X error handler. Keep the name the same because people are used to
+ * breaking on it in the debugger.
+ */
+static int
+gdk_x_error (Display *xdisplay,
+ XErrorEvent *error)
+{
+ if (error->error_code)
+ {
+ GdkDisplay *error_display;
+ GdkDisplayManager *manager;
+ GSList *displays;
+
+ /* Figure out which GdkDisplay if any got the error. */
+ error_display = NULL;
+ manager = gdk_display_manager_get ();
+ displays = gdk_display_manager_list_displays (manager);
+ while (displays != NULL)
+ {
+ GdkDisplayX11 *gdk_display = displays->data;
+
+ if (xdisplay == gdk_display->xdisplay)
+ {
+ error_display = GDK_DISPLAY_OBJECT (gdk_display);
+ g_slist_free (displays);
+ displays = NULL;
+ }
+ else
+ {
+ displays = g_slist_delete_link (displays, displays);
+ }
+ }
+
+ if (error_display == NULL)
+ {
+ /* Error on an X display not opened by GDK. Ignore. */
+
+ return 0;
+ }
+ else
+ {
+ _gdk_x11_display_error_event (error_display, error);
+ }
+ }
+
+ return 0;
+}
+
void
-gdk_error_trap_push (void)
+_gdk_x11_error_handler_push (void)
{
- GdkErrorTrap *trap;
+ _gdk_old_error_handler = XSetErrorHandler (gdk_x_error);
- trap = g_slice_new (GdkErrorTrap);
+ if (_gdk_error_handler_push_count > 0)
+ {
+ if (_gdk_old_error_handler != gdk_x_error)
+ g_warning ("XSetErrorHandler() called with a GDK error trap pushed. Don't do that.");
+ }
- trap->old_handler = XSetErrorHandler (gdk_x_error);
- trap->error_code = _gdk_error_code;
- trap->error_warnings = _gdk_error_warnings;
+ _gdk_error_handler_push_count += 1;
+}
+
+void
+_gdk_x11_error_handler_pop (void)
+{
+ g_return_if_fail (_gdk_error_handler_push_count > 0);
+
+ _gdk_error_handler_push_count -= 1;
+
+ if (_gdk_error_handler_push_count == 0)
+ {
+ XSetErrorHandler (_gdk_old_error_handler);
+ _gdk_old_error_handler = NULL;
+ }
+}
+
+/**
+ * gdk_error_trap_push:
+ *
+ * This function allows X errors to be trapped instead of the normal
+ * behavior of exiting the application. It should only be used if it
+ * is not possible to avoid the X error in any other way. Errors are
+ * ignored on all #GdkDisplay currently known to the
+ * #GdkDisplayManager. If you don't care which error happens and just
+ * want to ignore everything, pop with gdk_error_trap_pop_ignored().
+ * If you need the error code, use gdk_error_trap_pop() which may have
+ * to block and wait for the error to arrive from the X server.
+ *
+ * This API exists on all platforms but only does anything on X.
+ *
+ * You can use gdk_x11_display_error_trap_push() to ignore errors
+ * on only a single display.
+ *
+ * <example>
+ * <title>Trapping an X error</title>
+ * <programlisting>
+ * gdk_error_trap_push (<!-- -->);
+ *
+ * // ... Call the X function which may cause an error here ...
+ *
+ *
+ * if (gdk_error_trap_pop (<!-- -->))
+ * {
+ * // ... Handle the error here ...
+ * }
+ * </programlisting>
+ * </example>
+ *
+ */
+void
+gdk_error_trap_push (void)
+{
+ GdkGlobalErrorTrap *trap;
+ GdkDisplayManager *manager;
+ GSList *tmp_list;
+
+ trap = g_slice_new (GdkGlobalErrorTrap);
+ manager = gdk_display_manager_get ();
+ trap->displays = gdk_display_manager_list_displays (manager);
+
+ g_slist_foreach (trap->displays, (GFunc) g_object_ref, NULL);
+ for (tmp_list = trap->displays;
+ tmp_list != NULL;
+ tmp_list = tmp_list->next)
+ {
+ gdk_x11_display_error_trap_push (tmp_list->data);
+ }
g_queue_push_head (&gdk_error_traps, trap);
- _gdk_error_code = 0;
- _gdk_error_warnings = 0;
}
-gint
-gdk_error_trap_pop (void)
+static gint
+gdk_error_trap_pop_internal (gboolean need_code)
{
- GdkErrorTrap *trap;
+ GdkGlobalErrorTrap *trap;
gint result;
+ GSList *tmp_list;
trap = g_queue_pop_head (&gdk_error_traps);
- g_return_val_if_fail (trap != NULL, 0);
+ g_return_val_if_fail (trap != NULL, Success);
+
+ result = Success;
+ for (tmp_list = trap->displays;
+ tmp_list != NULL;
+ tmp_list = tmp_list->next)
+ {
+ gint code = Success;
+
+ if (need_code)
+ code = gdk_x11_display_error_trap_pop (tmp_list->data);
+ else
+ gdk_x11_display_error_trap_pop_ignored (tmp_list->data);
- result = _gdk_error_code;
+ /* we use the error on the last display listed, why not. */
+ if (code != Success)
+ result = code;
+ }
- _gdk_error_code = trap->error_code;
- _gdk_error_warnings = trap->error_warnings;
- XSetErrorHandler (trap->old_handler);
+ g_slist_foreach (trap->displays, (GFunc) g_object_unref, NULL);
+ g_slist_free (trap->displays);
- g_slice_free (GdkErrorTrap, trap);
+ g_slice_free (GdkGlobalErrorTrap, trap);
return result;
}
+/**
+ * gdk_error_trap_pop_ignored:
+ *
+ * Removes an error trap pushed with gdk_error_trap_push(), but
+ * without bothering to wait and see whether an error occurred. If an
+ * error arrives later asynchronously that was triggered while the
+ * trap was pushed, that error will be ignored.
+ *
+ * Since: 3.0
+ */
+void
+gdk_error_trap_pop_ignored (void)
+{
+ gdk_error_trap_pop_internal (FALSE);
+}
+
+/**
+ * gdk_error_trap_pop:
+ *
+ * Removes an error trap pushed with gdk_error_trap_push().
+ * May block until an error has been definitively received
+ * or not received from the X server. gdk_error_trap_pop_ignored()
+ * is preferred if you don't need to know whether an error
+ * occurred, because it never has to block. If you don't
+ * need the return value of gdk_error_trap_pop(), use
+ * gdk_error_trap_pop_ignored().
+ *
+ * Prior to GDK 3.0, this function would not automatically
+ * sync for you, so you had to gdk_flush() if your last
+ * call to Xlib was not a blocking round trip.
+ *
+ * Return value: X error code or 0 on success
+ */
+gint
+gdk_error_trap_pop (void)
+{
+ return gdk_error_trap_pop_internal (TRUE);
+}
+
gchar *
gdk_get_display (void)
{
GdkScreen *screen;
};
+void _gdk_x11_error_handler_push (void);
+void _gdk_x11_error_handler_pop (void);
void _gdk_xid_table_insert (GdkDisplay *display,
XID *xid,
void gdk_x11_display_grab (GdkDisplay *display);
void gdk_x11_display_ungrab (GdkDisplay *display);
+
+void gdk_x11_display_error_trap_push (GdkDisplay *display);
+/* warn unused because you could use pop_ignored otherwise */
+G_GNUC_WARN_UNUSED_RESULT gint gdk_x11_display_error_trap_pop (GdkDisplay *display);
+void gdk_x11_display_error_trap_pop_ignored (GdkDisplay *display);
+
void gdk_x11_register_standard_event_type (GdkDisplay *display,
gint event_base,
gint n_events);